﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Cci.MutableCodeModel;

namespace Microsoft.Cci.Contrib.Mutators
{
    public class ForStatementMutator: MethodBodyCodeMutator
    {
        public ForStatementMutator(IMetadataReaderHost host):base(host)
        {
        }
        public override IBlockStatement Visit(BlockStatement blockStatement)
        {
            var xBlock = base.Visit(blockStatement);
            if (xBlock.Statements.Count() < 4)
            {
                return xBlock;
            }          
            var xStatements = xBlock.Statements.ToList();
            var xExpression = xStatements.First() as IExpressionStatement;
            if (xExpression == null)
            {
                return xBlock;
            }
            var xAssignment = xExpression.Expression as IAssignment;
            if (xAssignment == null)
            {
                return xBlock;
            }
            var xLocalDecl = xAssignment.Target.Definition as ILocalDefinition;
            if (xLocalDecl == null)
            {
                return xBlock;
            }
            if (!CanBeCounterVariable(xLocalDecl))
            {
                return xBlock;
            }
            var xGotoStatement1 = xStatements[1] as IGotoStatement;
            if (xGotoStatement1 == null)
            {
                return xBlock;
            }
            IStatement xGotoStatement1TargetStatement = null;
            int xStartForBlock = 2;
            int xEndForBlock = 2;
            for (int i = 2; i < (xStatements.Count-1); i++)
            {
                var xLabeledStatement = xStatements[i] as ILabeledStatement;
                if (xLabeledStatement == null)
                {
                    continue;
                }
                if (xLabeledStatement.Label.Value == xGotoStatement1.TargetStatement.Label.Value)
                {
                    xGotoStatement1TargetStatement = xStatements[i+1];
                    xEndForBlock = i - 2;
                    break;
                }
            }
            if (xGotoStatement1TargetStatement == null)
            {
                return xBlock;
            }
            var xConditionalStatement = xGotoStatement1TargetStatement as IConditionalStatement;
            if (xConditionalStatement == null)
            {
                return xBlock;
            }
            var xLessThan = xConditionalStatement.Condition as ILessThan;
            if (xLessThan == null)
            {
                return xBlock;
            }
            var xBoundExpresssionLeft = xLessThan.LeftOperand as IBoundExpression;
            if (xBoundExpresssionLeft == null)
            {
                return xBlock;
            }
            if (xBoundExpresssionLeft.Definition != xLocalDecl)
            {
                return xBlock;
            }
            var xForStatement = new ForStatement();
            xForStatement.Condition = xConditionalStatement.Condition;
            xForStatement.InitStatements.Add(xExpression);
            var xBody = new BlockStatement();
            for(int i = xStartForBlock+1; i <= (xEndForBlock);i++){
                xBody.Statements.Add(xStatements[xStartForBlock+1]);
                if (i < xEndForBlock)
                {
                    xStatements.RemoveAt(xStartForBlock + 1);
                }
            }
            xStatements.RemoveRange(xStartForBlock, 2);
            xForStatement.Body = xBody;
            xStatements.RemoveRange(0, 2);
            xForStatement.IncrementStatements.Add(xStatements[0]);
            xStatements.RemoveRange(0,3);
            var xReturn = new BlockStatement();
            xReturn.Statements.Add(xForStatement);
            xReturn.Statements.AddRange(xStatements);
            return base.Visit(xReturn);
        }

        private bool CanBeCounterVariable(ILocalDefinition local)
        {
            return local.Type.ToString() == typeof(Int32).FullName;
        }
    }
}